/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ia.tpfinal;

import ia.tpfinal.jmetal.base.*;
import ia.tpfinal.jmetal.base.variable.Permutation;
import ia.tpfinal.moacofinal.ConjuntoPareto;
import ia.tpfinal.moacofinal.Problem;
import ia.tpfinal.moacofinal.Solucion;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author HP
 */
public class Util {

    public List<Double[]> getDatosFromArchivo(String fileName) throws FileNotFoundException, IOException {
        List<Double[]> objetivos = new ArrayList<Double[]>();
        BufferedReader inputFile = new BufferedReader(
                new InputStreamReader(
                new FileInputStream(fileName)));
        StreamTokenizer token = new StreamTokenizer(inputFile);
        String str = " ";
        Double[] objs = new Double[2];
        str = inputFile.readLine();
        String[] objsStr;
        while (str != null && str.length() != 0) {
            objs = new Double[2];
            objsStr = new String[2];
            objsStr = str.split("\t");
            objs[0] = Double.parseDouble(objsStr[0]);
            objs[1] = Double.parseDouble(objsStr[1]);
            objetivos.add(objs);
            str = inputFile.readLine();

        }
        return objetivos;
    }

    public int compararFrentes(List<Double[]> front1, List<Double[]> front2) {

        double o1f1 = 0;
        double o1f2 = 0;
        double o2f1 = 0;
        double o2f2 = 0;
        double[] covFront1 = new double[front1.size()];
        double[] covFront2 = new double[front2.size()];
        for (int i = 0; i < front1.size(); i++) {
            for (int j = 0; j < front2.size(); j++) {
                o1f1 = front1.get(i)[0];
                o1f2 = front2.get(j)[0];
                o2f1 = front1.get(i)[1];
                o2f2 = front2.get(j)[1];

                if ((o1f1 < o1f2 && o2f1 <= o2f2) || (o1f1 <= o1f2 && o2f1 < o2f2)) { //si f1 domina a f2
                    covFront1[i]++;
                    //System.out.println("f1 domina a f2 - covFront1: " + covFront1[i] );
                } else if ((o1f2 < o1f1 && o2f2 <= o2f1) || (o1f2 <= o1f1 && o2f2 < o2f1)) {//si f2 domina a f1
                    covFront2[j]++;
                    //System.out.println("f2 domina a f1 - covFront2: " + covFront2[j]);
                }

            }
        }
        double covTotalFront1 = 0;
        double covTotalFront2 = 0;
        for (int i = 0; i < covFront1.length; i++) {
            covTotalFront1 = covTotalFront1 + (covFront1[i] / front2.size());
        }
        for (int i = 0; i < covFront2.length; i++) {
            covTotalFront2 = covTotalFront2 + (covFront2[i] / front1.size());
        }
        if (covTotalFront1 > covTotalFront2) {
            return 1;
        } else if (covTotalFront2 > covTotalFront1) {
            return 2;
        } else {
            //System.out.println("coverageF1: "+covTotalFront1);
            //System.out.println("coverageF2: "+covTotalFront2);
            return 0;
        }

    }

    /**
     *
     * @param punto1 Punto del frente actual
     * @param punto2 Punto del Ytrue
     * @return distancia entre dos puntos
     */
    public double calcularD(Double[] punto1, Double[] punto2) {
        double d;
        double x1 = punto1[0].doubleValue();
        double x2 = punto2[0].doubleValue();
        double y1 = punto1[1].doubleValue();
        double y2 = punto2[1].doubleValue();
        //System.out.println("x1: "+x1+" - y1: "+y1);
        //System.out.println("x2: "+x2+" - y2: "+y2);
        double potRestaX = Math.pow(x2 - x1, 2);
        double potRestaY = Math.pow(y2 - y1, 2);
        d = Math.pow(potRestaX + potRestaY, 0.5);
        return d;
    }

    /**
     * Este método se encarga de encontrar el mayor valor de las evaluaciones
     * para cada objetivo, para luego dividir cada una de esas evaluaciones por
     * el mayor valor. De esta forma se normalizan los valores de las
     * evaluaciones antes de aplicarle las métricas.
     *
     * @param front1
     * @return copia del frente, pero normalizado
     */
    public List<Double[]> normalizar(List<Double[]> front1) {
        double mayorVal1 = 0;
        double mayorVal2 = 0;
        List<Double[]> result = new ArrayList<Double[]>();
        // obtiene los valores mayores
        for (Double[] val : front1) {
            if (val[0] > mayorVal1) {
                mayorVal1 = val[0];
            }
            if (val[1] > mayorVal2) {
                mayorVal2 = val[1];
            }
        }
        //carga la copia y nromaliza
        for (Double[] sol : front1) {
            Double[] solucion = {0.0, 0.0};
            solucion[0] = (double) sol[0] / mayorVal1;
            solucion[1] = (double) sol[1] / mayorVal2;
            result.add(solucion);
        }
        return result;
    }

    /**
     *
     * @param front1 Frente actual
     * @param front2 Ytrue
     * @return Metrica de comparacion M1 (distancia al Ytrue)
     */
    public double calcularM1(List<Double[]> front1, List<Double[]> front2) {
        //List<Double[]> front1 = this.normalizar(Front1);
        //List<Double[]> front2 = this.normalizar(Front2);
        double sum = 0;
        for (Double[] punto1 : front1) {
            double dist = 10000000000.0;
            double distActual;
            for (Double[] punto2 : front2) {
                distActual = this.calcularD(punto1, punto2);
                //System.out.println("Punto1: "+punto1[0]+" - "+punto1[1]);
                //System.out.println("Punto2: "+punto2[0]+" - "+punto2[1]);
                if (distActual < dist) {
                    dist = distActual;
                }
            }
            sum += dist;
            dist = 10000000000.0;
        }
        return (sum / front1.size());
    }

    /**
     *
     * @param front1 Frente actual
     * @return Metrica de comparacion M2 (distribucion)
     */
    public double calcularM2(List<Double[]> front1) {
        //List<Double[]> front1 = this.normalizar(Front1);
        double sum = 0;
        double rho = 0.5; //falta establecer este parametro
        for (Double[] punto1 : front1) {
            double distActual = 0;
            for (Double[] punto2 : front1) {
                distActual = this.calcularD(punto1, punto2);
                if (distActual > rho) {
                    sum++;
                }
            }
        }
        return (sum / (front1.size() + 1));
    }

    /**
     *
     * @param front1 Frente actual
     * @return Metrica de comparacion M3 (extension)
     */
    public double calcularM3(List<Double[]> front1) {
        //List<Double[]> front1 = this.normalizar(Front1);
        double mayorX = 0;
        double mayorY = 0;
        double menorX = 10000000000.0;
        double menorY = 10000000000.0;
        Double[] mejorPunto1 = {0.0, 0.0};
        Double[] mejorPunto2 = {0.0, 0.0};
        for (Double[] punto1 : front1) {
            if (punto1[0].doubleValue() > mayorX && punto1[1].doubleValue() < menorY) {
                mayorX = punto1[0].doubleValue();
                menorY = punto1[1].doubleValue();
                mejorPunto1[0] = punto1[0];
                mejorPunto1[1] = punto1[1];
            }
        }
        //System.out.println("Punto1: "+mejorPunto1[0]+" - "+mejorPunto1[1]);
        for (Double[] punto1 : front1) {
            if (punto1[0].doubleValue() < menorX && punto1[1].doubleValue() > mayorY) {
                menorX = punto1[0].doubleValue();
                mayorY = punto1[1].doubleValue();
                mejorPunto2[0] = punto1[0];
                mejorPunto2[1] = punto1[1];
            }
        }
        //System.out.println("Punto2: "+mejorPunto2[0]+" - "+mejorPunto2[1]);
        return this.calcularD(mejorPunto1, mejorPunto2);
    }

    /**
     *
     * @param front1 Frente actual
     * @param front2 Ytrue
     * @return Porcentaje de error
     */
    public double calcularError(List<Double[]> front1, List<Double[]> front2) {
        int intersec = 0;
        for (Double[] punto1 : front1) {
            for (Double[] punto2 : front2) {
                //System.out.println("Punto1: "+punto1[0]+" - "+punto1[1]);
                //System.out.println("Punto2: "+punto2[0]+" - "+punto2[1]);
                if (punto1[0].doubleValue() == punto2[0].doubleValue() && punto1[1].doubleValue() == punto2[1].doubleValue()) {
                    intersec++;
                }
            }
        }
        // System.out.println("Intersec: "+intersec+" Size del frente: "+front1.size()+" Division "+(intersec / (double) front1.size())+" Resta "+(1.0000 - (intersec / (double)front1.size())));
        return ((double) 1 - (intersec / (double) front1.size())) /**
                 * (double) 100
                 */
                ;
    }

    public SolutionSet convertirPareto(Problem prob, ConjuntoPareto paretoViejo) {
        SolutionSet paretoNuevo = new SolutionSet(500);
        for (int i = 0; i < paretoViejo.getSize(); i++) {
            Solucion solucion = paretoViejo.getSolucion(i);
            Permutation per = new Permutation(solucion.getArray());
            DecisionVariables dv = new DecisionVariables(per);
            Solution sol = new Solution(dv);
            sol.setObjective(0, prob.funcion_obj_1(paretoViejo.lista[i]));
            sol.setObjective(1, prob.funcion_obj_2(paretoViejo.lista[i]));
            paretoNuevo.add(sol);
        }
        return paretoNuevo;
    }

    public void calcularMetricas(String path, List<Double[]> YtrueM, String[] frontPath, String instancia) {
        try {
            List<Double[]> frenteActual;
            double m1;
            double m2;
            double m2Ytrue = this.calcularM2(YtrueM);
            double m3;
            double m3Ytrue = this.calcularM3(YtrueM);
            double error;
            FileOutputStream fos = new FileOutputStream(path, true);
            OutputStreamWriter osw = new OutputStreamWriter(fos);
            BufferedWriter bw = new BufferedWriter(osw);
            bw.write("Instancia: " + instancia.substring(0, instancia.length() - 1));
            bw.newLine();
            bw.write("-------------------------------------");
            bw.newLine();
            for (int i = 0; i < 10; i++) {

                frenteActual = this.getDatosFromArchivo(frontPath[i]);
                m1 = this.calcularM1(frenteActual, YtrueM);
                m2 = this.calcularM2(frenteActual);
                m3 = this.calcularM3(frenteActual);
                error = this.calcularError(frenteActual, YtrueM);
                bw.write("Resumen de metricas - Frente " + i);
                bw.newLine();

                bw.write("M1 = " + m1);
                bw.newLine();

                bw.write("M2(Frente) = " + m2 + " M2(Ytrue) = " + m2Ytrue);
                bw.newLine();
                bw.write("M3(Frente) = " + m3 + " M3(Ytrue) = " + m3Ytrue);
                bw.newLine();
                bw.write("Error = " + error + "%");
                bw.newLine();

            }
            /*
             * Close the file
             */
            bw.close();
        } catch (IOException e) {
            Configuration.logger_.severe("Error acceding to the file");
            e.printStackTrace();
        }
    }

    public static void main(String args[]) throws IOException {
        Util u = new Util();
        String[] frontPath = new String[10];
        for (int i = 0; i < 10; i++) {
            frontPath[i] = "src/ia/tpfinal/files/solucionesParaPaper/" + "FrontKROAB-" + i + ".txt";
        }

        List<Double[]> frenteActual;
        List<Double[]> YtrueM = u.getDatosFromArchivo("src/ia/tpfinal/files/solucionesParaPaper/yTrueKROAB" + ".txt"); // el que decimos Ytrue
        double[] ms1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
        double mayorM1 = 0;
        double m1;
        double m2;
        double m2Ytrue = u.calcularM2(YtrueM);
        double m3;
        double m3Ytrue = u.calcularM3(YtrueM);
        double error;
        for (int i = 0; i < 10; i++) {
            frenteActual = u.getDatosFromArchivo(frontPath[i]);
            ms1[i] = u.calcularM1(frenteActual, YtrueM);
            if (ms1[i] > mayorM1) {
                mayorM1 = ms1[i];
            }
        }
        for (int i = 0; i < 10; i++) {
            frenteActual = u.getDatosFromArchivo(frontPath[i]);
            //m1 = u.calcularM1(frenteActual, YtrueM);
            m1 = ms1[i];
            m2 = u.calcularM2(frenteActual);
            m3 = u.calcularM3(frenteActual);
            error = u.calcularError(frenteActual, YtrueM);
            System.out.println("Resumen de metricas - Frente " + i);
            //System.out.println("M1 = " + m1);
            //System.out.println("M2(Frente) = " + m2 + " M2(Ytrue) = " + m2Ytrue);
            //System.out.println("M3(Frente) = " + m3 + " M3(Ytrue) = " + m3Ytrue);
            System.out.println("M1 = " + ((double) 1 - ((double) m1 / mayorM1)));
            System.out.println("M2 = " + (double) m2 / m2Ytrue);
            System.out.println("M3 = " + (double) m3 / m3Ytrue);
            System.out.println("Error = " + error);
        }
    }
}
